
/*  目标：  省电  */
/*
    linux 电源管理：主要牵扯到系统级待机、频率电压变换、系统空闲时候的处理；
CPU运行时候，电源管理的整体架构：
    1.CPU在运行的时候，根据系统负载进行动态电压和频率变换的CPUFreq
    2.CPU在空闲时，根据空闲的情况进行低功耗模式的CPUIdle
    3.多核系统下的CPU热插拔功能
    4.系统和设备针对延迟的特别需求而提出申请的PM QoS,它会作用于CPUIdle的具体策略
    5.设备驱动针对系统挂起到RAM/硬盘的一系列入口函数
    6.Soc进入挂起状态，SDRAM自刷新入口
    7.设备的运行时动态电源管理，根据使用情况动态开关设备
    因为CPU的功耗和电压以及频率成正比， P = FV^2 ，所以减低电压和频率可以降低功耗
*/

/*
   CPUFreq:CPU频率管理
   CPUIdle:CPU空闲管理
*/

#include <linux/cpufreq.h>
#include <linux/clk.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/sysfs.h>


static unsigned long regulator_latency;


struct s3c64xx_dvfs {
	unsigned int vddarm_min;
	unsigned int vddarm_max;
};

static struct s3c64xx_dvfs s3c64xx_dvfs_table[] = {
	[0] = { 1000000, 1150000 },
	[1] = { 1050000, 1150000 },
	[2] = { 1100000, 1150000 },
	[3] = { 1200000, 1350000 },
	[4] = { 1300000, 1350000 },
};

static struct cpufreq_frequency_table s3c64xx_freq_table[] = {
	{ 0, 0,  66000 },
	{ 0, 0, 100000 },
	{ 0, 0, 133000 },
	{ 0, 1, 200000 },
	{ 0, 1, 222000 },
	{ 0, 1, 266000 },
	{ 0, 2, 333000 },
	{ 0, 2, 400000 },
	{ 0, 2, 532000 },
	{ 0, 2, 533000 },
	{ 0, 3, 667000 },
	{ 0, 4, 800000 },
	{ 0, 0, CPUFREQ_TABLE_END },
};

// 完成目标频率的函数
static int s3c64xx_cpufreq_set_target(struct cpufreq_policy *policy,
				      unsigned int index)
{
	struct s3c64xx_dvfs *dvfs;
	unsigned int old_freq, new_freq;
	int ret;

	old_freq = clk_get_rate(policy->clk) / 1000;
	// 从频率表中获取对应index的频率
	new_freq = s3c64xx_freq_table[index].frequency;
	dvfs = &s3c64xx_dvfs_table[s3c64xx_freq_table[index].driver_data];

	// 设置具体的频率和电压环节
	ret = clk_set_rate(policy->clk, new_freq * 1000);
	if (ret < 0) {
		pr_err("Failed to set rate %dkHz: %d\n",
		       new_freq, ret);
		return ret;
	}
	pr_debug("Set actual frequency %lukHz\n",
	clk_get_rate(policy->clk) / 1000);

	return 0;
}

static void s3c64xx_cpufreq_config_regulator(void)
{
	int count, v, i, found;
	struct cpufreq_frequency_table *freq;
	struct s3c64xx_dvfs *dvfs;

	count = regulator_count_voltages(vddarm);
	if (count < 0) {
		pr_err("Unable to check supported voltages\n");
	}

	if (!count)
		goto out;

	cpufreq_for_each_valid_entry(freq, s3c64xx_freq_table) {
		dvfs = &s3c64xx_dvfs_table[freq->driver_data];
		found = 0;

		for (i = 0; i < count; i++) {
			v = regulator_list_voltage(vddarm, i);
			if (v >= dvfs->vddarm_min && v <= dvfs->vddarm_max)
				found = 1;
		}

		if (!found) {
			pr_debug("%dkHz unsupported by regulator\n",
				 freq->frequency);
			freq->frequency = CPUFREQ_ENTRY_INVALID;
		}
	}

out:
	/* Guess based on having to do an I2C/SPI write; in future we
	 * will be able to query the regulator performance here. */
	regulator_latency = 1 * 1000 * 1000;
}


static int s3c64xx_cpufreq_driver_init(struct cpufreq_policy *policy)
{
	int ret;
	struct cpufreq_frequency_table *freq;

	if (policy->cpu != 0)
		return -EINVAL;

	if (s3c64xx_freq_table == NULL) {
		pr_err("No frequency information for this CPU\n");
		return -ENODEV;
	}

	policy->clk = clk_get(NULL, "armclk");
	if (IS_ERR(policy->clk)) {
		pr_err("Unable to obtain ARMCLK: %ld\n",
		       PTR_ERR(policy->clk));
		return PTR_ERR(policy->clk);
	}

#ifdef CONFIG_REGULATOR
	vddarm = regulator_get(NULL, "vddarm");
	if (IS_ERR(vddarm)) {
		ret = PTR_ERR(vddarm);
		pr_err("Failed to obtain VDDARM: %d\n", ret);
		pr_err("Only frequency scaling available\n");
		vddarm = NULL;
	} else {
		s3c64xx_cpufreq_config_regulator();
	}
#endif

	cpufreq_for_each_entry(freq, s3c64xx_freq_table) {
		unsigned long r;

		/* Check for frequencies we can generate */
		r = clk_round_rate(policy->clk, freq->frequency * 1000);
		r /= 1000;
		if (r != freq->frequency) {
			pr_debug("%dkHz unsupported by clock\n",
				 freq->frequency);
			freq->frequency = CPUFREQ_ENTRY_INVALID;
		}

		/* If we have no regulator then assume startup
		 * frequency is the maximum we can support. */
		if (!vddarm && freq->frequency > clk_get_rate(policy->clk) / 1000)
			freq->frequency = CPUFREQ_ENTRY_INVALID;
	}

	/* Datasheet says PLL stabalisation time (if we were to use
	 * the PLLs, which we don't currently) is ~300us worst case,
	 * but add some fudge.
	 */
	ret = cpufreq_generic_init(policy, s3c64xx_freq_table,
			(500 * 1000) + regulator_latency);
	if (ret != 0) {
		pr_err("Failed to configure frequency table: %d\n",
		       ret);
		regulator_put(vddarm);
		clk_put(policy->clk);
	}

	return ret;
}

static struct cpufreq_driver s3c64xx_cpufreq_driver = {
	.flags		= CPUFREQ_NEED_INITIAL_FREQ_CHECK,
	.verify		= cpufreq_generic_frequency_table_verify,
	.target_index	= s3c64xx_cpufreq_set_target,
	.get		= cpufreq_generic_get,
	.init		= s3c64xx_cpufreq_driver_init,
	.name		= "s3c",
};

static int __init s3c64xx_cpufreq_init(void)
{
    /* 注册一个 cpufreq 实例 */
	return cpufreq_register_driver(&s3c64xx_cpufreq_driver);
}
module_init(s3c64xx_cpufreq_init);




/* 
    CFreq 通知：
    CPUFreq发出通知的情况只有两种： CPUFreq 策略发生变化，或则CPUFreq频率发生变化。
    CPUFreq策略发生变化时，CPUFreq会发出CPUFREQ_POLICY_CHANGED通知，并传递新的策略。
    CPUFreq频率发生变化时，CPUFreq会发出CPUFREQ_FREQ_CHANGED通知，并传递新的频率。

*/
/*  处理策略变化以及频率变化的函数 */
static int cpufreq_policy_change_handler(struct notifier_block *nb, unsigned long event, void *data)
{
    struct cpufreq_policy *policy = data;

    if (event == CPUFREQ_POLICY_CHANGED) {
        // 处理策略变化
        printk(KERN_INFO "CPUFREQ_POLICY_CHANGED: new policy min: %u, max: %u\n", policy->min, policy->max);
    }

    return NOTIFY_OK;
}
static int cpufreq_freq_change_handler(struct notifier_block *nb, unsigned long event, void *data)
{
    struct cpufreq_freqs *freqs = data;

    if (event == CPUFREQ_FREQ_CHANGED) {
        // 处理频率变化
        printk(KERN_INFO "CPUFREQ_FREQ_CHANGED: new frequency: %u\n", freqs->new);
    }

    return NOTIFY_OK;
}
static struct notifier_block cpufreq_policy_notifier = {
    .notifier_call = cpufreq_policy_change_handler,
};
static struct notifier_block cpufreq_freq_notifier = {
    .notifier_call = cpufreq_freq_change_handler,
};
static int __init s3c64xx_cpufreq_init(void)
{
    int ret;
    ret = cpufreq_register_notifier(&cpufreq_policy_notifier, CPUFREQ_POLICY_NOTIFIER);
    if (ret) {
        printk(KERN_ERR "Failed to register cpufreq policy notifier\n");
        return ret;
    }
    ret = cpufreq_register_notifier(&cpufreq_freq_notifier, CPUFREQ_TRANSITION_NOTIFIER);
    if (ret) {
        printk(KERN_ERR "Failed to register cpufreq freq notifier\n");
        cpufreq_unregister_notifier(&cpufreq_policy_notifier, CPUFREQ_POLICY_NOTIFIER);
        return ret;
    }
    return 0;
}



/*
    cpuidle : 空闲管理 (Idle Management)
    cpuidle_register_driver(struct cpuidle_driver *driver) : 注册一个 cpuidle 实例
    其实cpu的空闲管理和频率管理类似，都是通过注册一个驱动来实现的。
    不同的点在于，频率管理是针对CPU的，而空闲管理是针对整个系统的。

*/

/*
    CPU热插拔：
    CPU热插拔是指在不关闭系统的情况下，动态地添加或移除CPU。
    CPU热插拔的实现需要硬件和操作系统的支持，并且需要相应的驱动程序来处理。
    在Linux内核中，CPU热插拔的实现是通过cpufreq和cpuidle来完成的。
    cpufreq负责管理CPU的频率，而cpuidle负责管理CPU的空闲状态。
    当CPU被添加或移除时，cpufreq和cpuidle会自动调整CPU的频率和空闲状态，以适应新的CPU配置。
*/
